/*
 * Copyright (c) 2025 HiEasyX
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/**
 * \file hex_slider.cpp
 * \brief The value slider control for HiEasyX
 */

#include <include/hex.h>
#include <include/hex_slider.h>

#include <format>

#include <iostream>

namespace HX {
template <class Type>
void Slider(const HXString &Title, Type &Value, SliderProfile<Type> &Profile) {
	if (RoughVisibility()) {
		return;
	}

	if (Profile.Width == -1) {
		Profile.Width = 300;
	}
	auto &context = GetContext();
	auto &theme   = GetTheme();

	auto painter = CurrentPainter();

	constexpr HXGInt height = 22;

	auto position      = CurrentLayoutPosition();
	auto textRectangle = painter->MeasureText(Title, HXFont{}, 18);
	auto rectangle     = HXRect{position.X, position.Y, position.X + Profile.Width, position.Y + height};

	if (AccVisibility({rectangle.CalWidth() + textRectangle.Right, rectangle.CalHeight()})) {
		return;
	}

	auto percentage = static_cast<float>(Value - Profile.MinValue) / static_cast<float>(
		                  Profile.MaxValue - Profile.MinValue);

	// Process the presse or hover
	for (auto &Message : context.MessageQuery) {
		auto mouse = ClipCoord({Message.MouseX, Message.MouseY});
		if (Message.Processed) {
			continue;
		}
		if (Message.MouseLeftPressed && rectangle.PointInside(mouse)) {
			Message.Processed = true;

			percentage     = static_cast<float>(mouse.X - position.X) / Profile.Width;
			Profile.InDrag = true;
		}
		if (Message.MouseAction && Profile.InDrag) {
			Message.Processed = true;

			percentage = static_cast<float>(mouse.X - position.X) / Profile.Width;
		}
		if (Message.MouseLeftRelease && Profile.InDrag) {
			Profile.InDrag = false;
		}
	}

	if (percentage > 1.0) {
		percentage = 1.0;
	}
	if (percentage < 0.0) {
		percentage = 0.0;
	}

	Value = static_cast<Type>(percentage * (Profile.MaxValue - Profile.MinValue) + Profile.MinValue);

	painter->Begin();

	if (Profile.InDrag) {
		painter->DrawFilledRectangle(rectangle, theme.SliderActiveBackground,
		                             theme.SliderActiveBackground);
		painter->DrawText(Title, HXFont{},
		                  {rectangle.Right + 5,
		                   rectangle.Top + 4}, theme.SliderActiveText
		                  , 18);
	} else {
		painter->DrawFilledRectangle(rectangle, theme.SliderBackground,
		                             theme.SliderBackground);
		painter->DrawText(Title, HXFont{},
		                  {rectangle.Right + 5,
		                   rectangle.Top + 4}, theme.SliderText
		                  , 18);
	}

	auto boxRectangle = HXRect{static_cast<HXGInt>(rectangle.Left + percentage * Profile.Width - 5),
	                           rectangle.Top,
	                           static_cast<HXGInt>(rectangle.Left + percentage * Profile.Width + 5),
	                           rectangle.Bottom};
	if (boxRectangle.Left < position.X) {
		boxRectangle.Left  = position.X;
		boxRectangle.Right = position.X + 10;
	}
	if (boxRectangle.Right > position.X + Profile.Width) {
		boxRectangle.Left  = position.X + Profile.Width - 10;
		boxRectangle.Right = position.X + Profile.Width;
	}

#ifdef UNICODE
	auto valueString = std::to_wstring(Value);
#else
	auto valueString = std::to_string(Value);
#endif
	auto valueTextRectangle = painter->MeasureText(valueString, HXFont{}, 18);

	painter->DrawFilledRectangle(boxRectangle, theme.SliderBox,
	                             theme.SliderBox);
	painter->DrawText(valueString, HXFont{}, {
		                  position.X + Profile.Width / 2 - valueTextRectangle.Right / 2,
		                  position.Y + height / 2 - valueTextRectangle.Bottom / 2,},
	                  theme.SliderText, 18);

	painter->End();

	LayoutNextControl({rectangle.CalWidth() + textRectangle.Right, rectangle.CalHeight()});
}

void Slider1f(const HXString &Title, float &Value, SliderProfile1f &Profile) {
	return Slider<float>(Title, Value, Profile);
}

void Slider1i(const HXString &Title, int &Value, SliderProfile1i &Profile) {
	return Slider<int>(Title, Value, Profile);
}

void DrawSliderTitle(const HXString &Title) {
	if (RoughVisibility()) {
		return;
	}

	// Drawing title
	auto &context = GetContext();
	auto &theme   = GetTheme();

	auto position = CurrentLayoutPosition();

	auto painter = CurrentPainter();

	auto textRectangle = painter->MeasureText(Title, HXFont{}, 18);

	if (AccVisibility({textRectangle.Right, textRectangle.Bottom})) {
		return;
	}

	painter->Begin();

	painter->DrawText(Title, HXFont{},
	                  {position.X,
	                   position.Y + 4}, theme.SliderActiveText
	                  , 18);

	painter->End();

	LayoutNextControl({textRectangle.Right, textRectangle.Bottom});
}

void Slider2f(const HXString &Title, float (&Value)[2], SliderProfile2f &Profile) {
	BeginSameLine();

	if (Profile[0].Width == -1) {
		Profile[0].Width = 150;
	}
	if (Profile[1].Width == -1) {
		Profile[1].Width = 150;
	}

	Slider1f(HXStr(""), Value[0], Profile[0]);
	Slider1f(HXStr(""), Value[1], Profile[1]);

	DrawSliderTitle(Title);

	EndSameLine();
}

void Slider3f(const HXString &Title, float (&Value)[3], SliderProfile3f &Profile) {
	BeginSameLine();

	if (Profile[0].Width == -1) {
		Profile[0].Width = 100;
	}
	if (Profile[1].Width == -1) {
		Profile[1].Width = 100;
	}
	if (Profile[2].Width == -1) {
		Profile[2].Width = 100;
	}

	Slider1f(HXStr(""), Value[0], Profile[0]);
	Slider1f(HXStr(""), Value[1], Profile[1]);
	Slider1f(HXStr(""), Value[2], Profile[2]);

	DrawSliderTitle(Title);

	EndSameLine();
}

void Slider4f(const HXString &Title, float (&Value)[4], SliderProfile4f &Profile) {
	BeginSameLine();

	if (Profile[0].Width == -1) {
		Profile[0].Width = 75;
	}
	if (Profile[1].Width == -1) {
		Profile[1].Width = 75;
	}
	if (Profile[2].Width == -1) {
		Profile[2].Width = 75;
	}
	if (Profile[3].Width == -1) {
		Profile[3].Width = 75;
	}

	Slider1f(HXStr(""), Value[0], Profile[0]);
	Slider1f(HXStr(""), Value[1], Profile[1]);
	Slider1f(HXStr(""), Value[2], Profile[2]);
	Slider1f(HXStr(""), Value[3], Profile[3]);

	DrawSliderTitle(Title);

	EndSameLine();
}

void Slider2i(const HXString &Title, int (&Value)[2], SliderProfile2i &Profile) {
	BeginSameLine();

	if (Profile[0].Width == -1) {
		Profile[0].Width = 150;
	}
	if (Profile[1].Width == -1) {
		Profile[1].Width = 150;
	}

	Slider1i(HXStr(""), Value[0], Profile[0]);
	Slider1i(HXStr(""), Value[1], Profile[1]);

	DrawSliderTitle(Title);

	EndSameLine();
}

void Slider3i(const HXString &Title, int (&Value)[3], SliderProfile3i &Profile) {
	BeginSameLine();

	if (Profile[0].Width == -1) {
		Profile[0].Width = 100;
	}
	if (Profile[1].Width == -1) {
		Profile[1].Width = 100;
	}
	if (Profile[2].Width == -1) {
		Profile[2].Width = 100;
	}

	Slider1i(HXStr(""), Value[0], Profile[0]);
	Slider1i(HXStr(""), Value[1], Profile[1]);
	Slider1i(HXStr(""), Value[2], Profile[2]);

	DrawSliderTitle(Title);

	EndSameLine();
}

void Slider4i(const HXString &Title, int (&Value)[4], SliderProfile4i &Profile) {
	BeginSameLine();

	if (Profile[0].Width == -1) {
		Profile[0].Width = 75;
	}
	if (Profile[1].Width == -1) {
		Profile[1].Width = 75;
	}
	if (Profile[2].Width == -1) {
		Profile[2].Width = 75;
	}
	if (Profile[3].Width == -1) {
		Profile[3].Width = 75;
	}

	Slider1i(HXStr(""), Value[0], Profile[0]);
	Slider1i(HXStr(""), Value[1], Profile[1]);
	Slider1i(HXStr(""), Value[2], Profile[2]);
	Slider1i(HXStr(""), Value[3], Profile[3]);

	DrawSliderTitle(Title);

	EndSameLine();
}
}
